﻿using System.Web.Mvc;
using Nop.Plugin.Widgets.ExternalScripts.Models;
using Nop.Services.Configuration;
using Nop.Web.Framework.Controllers;
using System;
using System.Linq;
using System.Globalization;
using Nop.Core.Domain.Orders;
using Nop.Core;
using Nop.Services.Orders;
using Nop.Services.Logging;
using Nop.Services.Catalog;
using System.Text;
using Nop.Core.Domain;

namespace Nop.Plugin.Widgets.ExternalScripts.Controllers
{
    public class ExternalScriptsController : BasePluginController
    {
        private readonly IWorkContext _workContext;
        private readonly IStoreContext _storeContext;
        private readonly ISettingService _settingService;
        private readonly IOrderService _orderService;
        private readonly ICategoryService _categoryService;
        private readonly ILogger _logger;
        private readonly IWebHelper _webHelper;
        private readonly ExternalScriptsSettings _externalScriptsSettings;
        private readonly StoreInformationSettings _storeInformationSettings;

        CultureInfo usCulture = new CultureInfo("en-US");


        public ExternalScriptsController(IWorkContext workContext, IStoreContext storeContext,
            ISettingService settingService,
            IOrderService orderService,
            ICategoryService categoryService,
            ILogger logger, IWebHelper webHelper,
            ExternalScriptsSettings ExternalScriptsSettings, StoreInformationSettings storeInformationSettings)
        {
            this._storeContext = storeContext;
            this._workContext = workContext;
            this._settingService = settingService;
            this._orderService = orderService;
            this._categoryService = categoryService;
            this._logger = logger;
            this._webHelper = webHelper;
            this._externalScriptsSettings = ExternalScriptsSettings;
            this._storeInformationSettings = storeInformationSettings;
        }

        [AdminAuthorize]
        [ChildActionOnly]
        public ActionResult Configure()
        {
            var model = new ConfigurationModel();
            model.ZoneId = _externalScriptsSettings.WidgetZone;
            model.AvailableZones.Add(new SelectListItem() { Text = "<head> HTML tag", Value = "head_html_tag" });
            model.AvailableZones.Add(new SelectListItem() { Text = "Before <body> end HTML tag", Value = "body_end_html_tag_before" });

            model.ScriptSystemNames = _externalScriptsSettings.ScriptSystemNames.Split(';');



            return View("Nop.Plugin.Widgets.ExternalScripts.Views.ExternalScripts.Configure", model);
        }



        [HttpPost]
        [AdminAuthorize]
        [ChildActionOnly]
        public ActionResult Configure(ConfigurationModel model)
        {
            if (!ModelState.IsValid)
                return Configure();

            //save settings
            _externalScriptsSettings.WidgetZone = model.ZoneId;


            _settingService.SaveSetting(_externalScriptsSettings);

            return Configure();
        }

        [AdminAuthorize]
        public ActionResult ConfigureDetail(string scriptSystemName)
        {
            var model = new ConfigurationModel.ConfigurationDetailModel();

            model.ScriptSystemName = scriptSystemName;
            model.IsEnabled = _settingService.GetSettingByKey<bool>(string.Format("ExternalScriptsSettings.IsEnabled-{0}", scriptSystemName));
            model.ScriptDescription = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.ScriptDescription-{0}", scriptSystemName));
            model.DefaultScript = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.DefaultScript-{0}", scriptSystemName));
            model.CheckoutCompletedScript = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.CheckoutCompletedScript-{0}", scriptSystemName));
            model.CheckoutCompletedDetailScript = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.CheckoutCompletedDetailScript-{0}", scriptSystemName));
            model.ProductScript = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.ProductScript-{0}", scriptSystemName));
            model.CategoryScript = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.CategoryScript-{0}", scriptSystemName));
            model.ProductIdsAndFreeTokens = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.ProductIdsAndFreeTokens-{0}", scriptSystemName));
            model.CategoryIdsAndFreeTokens = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.CategoryIdsAndFreeTokens-{0}", scriptSystemName));
            model.ContollersAndFreeTokens = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.ContollersAndFreeTokens-{0}", scriptSystemName));
            model.PaymentMethodsLabels = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.PaymentMethodsLabels-{0}", scriptSystemName));
            model.ShippingMethodsLabels = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.ShippingMethodsLabels-{0}", scriptSystemName));

            return View("Nop.Plugin.Widgets.ExternalScripts.Views.ExternalScripts.ConfigureDetail", model);
        }

        [HttpPost]
        [AdminAuthorize]
        public ActionResult ConfigureDetail(ConfigurationModel.ConfigurationDetailModel model)
        {
            if (!ModelState.IsValid)
                return ConfigureDetail(model.ScriptSystemName);

            //save settings
            _settingService.SetSetting(string.Format("ExternalScriptsSettings.IsEnabled-{0}", model.ScriptSystemName), model.IsEnabled);
            _settingService.SetSetting(string.Format("ExternalScriptsSettings.DefaultScript-{0}", model.ScriptSystemName), model.DefaultScript);
            _settingService.SetSetting(string.Format("ExternalScriptsSettings.CheckoutCompletedScript-{0}", model.ScriptSystemName), model.CheckoutCompletedScript);
            _settingService.SetSetting(string.Format("ExternalScriptsSettings.CheckoutCompletedDetailScript-{0}", model.ScriptSystemName), model.CheckoutCompletedDetailScript);
            _settingService.SetSetting(string.Format("ExternalScriptsSettings.ProductIdsAndFreeTokens-{0}", model.ScriptSystemName), model.ProductIdsAndFreeTokens);
            _settingService.SetSetting(string.Format("ExternalScriptsSettings.ProductScript-{0}", model.ScriptSystemName), model.ProductScript);
            _settingService.SetSetting(string.Format("ExternalScriptsSettings.CategoryScript-{0}", model.ScriptSystemName), model.CategoryScript);
            _settingService.SetSetting(string.Format("ExternalScriptsSettings.CategoryIdsAndFreeTokens-{0}", model.ScriptSystemName), model.CategoryIdsAndFreeTokens);
            _settingService.SetSetting(string.Format("ExternalScriptsSettings.ContollersAndFreeTokens-{0}", model.ScriptSystemName), model.ContollersAndFreeTokens);
            _settingService.SetSetting(string.Format("ExternalScriptsSettings.PaymentMethodsLabels-{0}", model.ScriptSystemName), model.PaymentMethodsLabels);
            _settingService.SetSetting(string.Format("ExternalScriptsSettings.ShippingMethodsLabels-{0}", model.ScriptSystemName), model.ShippingMethodsLabels);


            var scriptSystemNames = _externalScriptsSettings.ScriptSystemNames.Split(';');
            if (!scriptSystemNames.Contains(model.ScriptSystemName))
            {
                _externalScriptsSettings.ScriptSystemNames = _externalScriptsSettings.ScriptSystemNames + ";" + model.ScriptSystemName;
                _settingService.SaveSetting(_externalScriptsSettings);
            }

            return View("Nop.Plugin.Widgets.ExternalScripts.Views.ExternalScripts.ConfigureDetail", model);
        }

        [ChildActionOnly]
        public ActionResult PublicInfo(string widgetZone)
        {
            string globalScript = "";
            try
            {

                foreach (var scriptSystemName in _externalScriptsSettings.ScriptSystemNames.Split(';'))
                {
                    globalScript += GetOneExternalScript(scriptSystemName);
                }

            }
            catch (Exception ex)
            {
                _logger.InsertLog(Core.Domain.Logging.LogLevel.Error, "Error creating external scripts", ex.ToString());
            }
            return Content(globalScript);
        }

        private string GetOneExternalScript(string scriptSystemName)
        {
            string externalScript = "";
            
            var isEnabled = _settingService.GetSettingByKey<bool>(string.Format("ExternalScriptsSettings.IsEnabled-{0}", scriptSystemName));
            if (!isEnabled)
            {
                return externalScript;
            }


            var routeData = ((System.Web.UI.Page)this.HttpContext.CurrentHandler).RouteData;
            string controller = routeData.Values["controller"].ToString();
            string action = routeData.Values["action"].ToString();
            if (controller.Equals("catalog", StringComparison.InvariantCultureIgnoreCase) &&
                action.Equals("category", StringComparison.InvariantCultureIgnoreCase))
            {
                var categoryScript = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.CategoryScript-{0}", scriptSystemName));
                if (string.IsNullOrEmpty(categoryScript))
                {
                    categoryScript = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.DefaultScript-{0}", scriptSystemName));
                }

                var categoryIdsAndFreeTokens = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings..CategoryIdsAndFreeTokens-{0}", scriptSystemName));
                //current category ID
                string currentCategoryIdStr = routeData.Values["categoryId"].ToString();
                String freeToken = GetFreeTokenForOneId(currentCategoryIdStr, categoryIdsAndFreeTokens);

                externalScript += GetDefaultScript(categoryScript, "", freeToken, null);

            }

            else if (controller.Equals("catalog", StringComparison.InvariantCultureIgnoreCase) &&
                action.Equals("product", StringComparison.InvariantCultureIgnoreCase))
            {
                var productScript = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.ProductScript-{0}", scriptSystemName));
                if (string.IsNullOrEmpty(productScript))
                {
                    productScript = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.DefaultScript-{0}", scriptSystemName));
                } 

                var productIdsAndFreeTokens = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.ProductIdsAndFreeTokens-{0}", scriptSystemName));
                //current product ID
                string currentProductIdStr = routeData.Values["productId"].ToString();
                String freeToken = GetFreeTokenForOneId(currentProductIdStr, productIdsAndFreeTokens);

                externalScript += GetDefaultScript(productScript, "", freeToken, null);
            }

            else if (controller.Equals("checkout", StringComparison.InvariantCultureIgnoreCase) &&
                action.Equals("completed", StringComparison.InvariantCultureIgnoreCase))

            {
                var ecommerceScript = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.CheckoutCompletedScript-{0}", scriptSystemName));
                var ecommerceDetailScript = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.CheckoutCompletedDetailScript-{0}", scriptSystemName));
                if (string.IsNullOrEmpty(ecommerceScript))
                {
                    ecommerceScript = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.DefaultScript-{0}", scriptSystemName));
                } 
                var paymentMethodsLabels = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.PaymentMethodsLabels-{0}", scriptSystemName));
                var shippingMethodsLabels = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.ShippingMethodsLabels-{0}", scriptSystemName));

                Order lastOrder = null;
                lastOrder = GetLastOrder();

                externalScript += GetDefaultScript(ecommerceScript, ecommerceDetailScript, "", lastOrder, paymentMethodsLabels, shippingMethodsLabels);
            }
            else
            {
                var defaultScript = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.DefaultScript-{0}", scriptSystemName));

                var contollersAndFreeTokens = _settingService.GetSettingByKey<string>(string.Format("ExternalScriptsSettings.ContollersAndFreeTokens-{0}", scriptSystemName));
                String freeToken = GetFreeTokenForController(controller, action, contollersAndFreeTokens);

                externalScript += GetDefaultScript(defaultScript, "", freeToken, null);
            }
            return externalScript;
        }

        Order lastOrder = null;
        private Order GetLastOrder()
        {
            if (lastOrder == null)
            {
                lastOrder = _orderService.SearchOrders(storeId: _storeContext.CurrentStore.Id,
                    customerId: _workContext.CurrentCustomer.Id, pageSize: 1).FirstOrDefault();
            }
            return lastOrder;
        }


        private String GetFreeTokenForController(string currentController, string currentAction, string controllersAndFreeTokens)
        {
            String freeToken = "";

            if (string.IsNullOrEmpty(controllersAndFreeTokens))
                return freeToken;

            bool founded = false;
            //controllersAndFreeTokens example "home/index,customer/info:ertERTert;contactus/index:klmKLMklm"
            string[] controllersAndFreeTokensSplitted = controllersAndFreeTokens.Split(';');//"home/index,customer/info:ertERTert" "contactus/index:klmKLMklm"

            foreach (var controllerAndFreeToken in controllersAndFreeTokensSplitted)
            {
                string[] controllersThenFreeToken = controllerAndFreeToken.Split(':');//"home/index,customer/info" "ertERTert"
                string[] controllers = controllersThenFreeToken[0].Split(',');//"home/index"  "customer/info"
                foreach (var controller in controllers)
                {
                    string[] controllerThenAction = controller.Split('/');//"home" "index"
                    if (controllerThenAction[0] == "*" || controllerThenAction[0].Equals(currentController, StringComparison.InvariantCultureIgnoreCase)
                        && (controllerThenAction.Length < 2 || string.IsNullOrEmpty(currentAction) || controllerThenAction[1] == "*" || controllerThenAction[1].Equals(currentAction, StringComparison.InvariantCultureIgnoreCase)))
                    {
                        freeToken = controllersThenFreeToken.Length == 1 ? " " : controllersThenFreeToken[1];
                        founded = true;
                    }
                    if (founded) break;
                }
                if (founded) break;
            }
            return freeToken;
        }

        private String GetFreeTokenForOneId(string currentIdStr, string idsAndFreeTokens)
        {
            String freeToken = "";

            if (string.IsNullOrEmpty(idsAndFreeTokens))
                return freeToken;

            bool founded = false;

            string[] idsAndFreeTokensSplitted = idsAndFreeTokens.Split(';');//"1,2:azeAZEaze" "3,4,5:rtyRTYrty"

            foreach (var idAndFreeToken in idsAndFreeTokensSplitted)
            {
                string[] idsThenFreeToken = idAndFreeToken.Split(':'); //"1,2" "azeAZEaze"
                string[] ids = idsThenFreeToken[0].Split(','); //"1" "2"
                foreach (var id in ids)
                {
                    if (id == "*" || id == currentIdStr)
                    {
                        freeToken = idsThenFreeToken.Length == 1 ? " " : idsThenFreeToken[1];
                        founded = true;
                    }
                    if (founded) break;
                }
                if (founded) break;
            }
            return freeToken;
        }

        private String GetLabelForValue(string currentValue, string valuesAndLabels)
        {
            String label = currentValue;

            if (string.IsNullOrEmpty(valuesAndLabels))
                return label;

            bool founded = false;

            string[] valuesAndLabelsSplitted = valuesAndLabels.Split(';');//"PaymentMethod.PayPal:paypal;PaymentMethod.Transfer:virement"

            foreach (var valueAndLabel in valuesAndLabelsSplitted)
            {
                string[] valueThenLabel = valueAndLabel.Split(':'); //"PaymentMethod.PayPal" "paypal"
                if (currentValue == valueThenLabel[0])
                {
                    label = valueThenLabel.Length == 1 ? currentValue : valueThenLabel[1];
                    founded = true;
                }
                if (founded) break;
            }
            return label;
        }



        private string GetDefaultScript(string script, string detailScript, string freeToken, Order order = null, string paymentMethodsLabels = "", string shippingMethodsLabels = "")
        {

            script += "\n";

            if (order == null)
            {
                script = script.Replace("{VALUE}", 0.ToString("0.00", usCulture));
            }
            else
            {
                script = script.Replace("{CUSTOMERID}", order.CustomerId.ToString());
                script = script.Replace("{ORDERID}", order.Id.ToString());
                script = script.Replace("{TOTALEXCLTAX}", (order.OrderTotal - order.OrderTax).ToString("0.00", usCulture));
                script = script.Replace("{TOTALINCLTAX}", order.OrderTotal.ToString("0.00", usCulture));
                script = script.Replace("{SUBTOTALEXCLTAX}", order.OrderSubtotalExclTax.ToString("0.00", usCulture));
                script = script.Replace("{SUBTOTALINCLTAX}", order.OrderSubtotalInclTax.ToString("0.00", usCulture));
                script = script.Replace("{SUBTOTALDISCOUNTEXCLTAX}", order.OrderSubTotalDiscountExclTax.ToString("0.00", usCulture));
                script = script.Replace("{SUBTOTALDISCOUNTINCLTAX}", order.OrderSubTotalDiscountInclTax.ToString("0.00", usCulture));
                script = script.Replace("{TAX}", order.OrderTax.ToString("0.00", usCulture));
                script = script.Replace("{SHIPPINGEXCLTAX}", order.OrderShippingExclTax.ToString("0.00", usCulture));
                script = script.Replace("{SHIPPINGINCLTAX}", order.OrderShippingInclTax.ToString("0.00", usCulture));
                script = script.Replace("{CITY}", order.BillingAddress == null ? "" : FixIllegalJavaScriptChars(order.BillingAddress.City));
                script = script.Replace("{STATEPROVINCE}", order.BillingAddress == null || order.BillingAddress.StateProvince == null ? "" : FixIllegalJavaScriptChars(order.BillingAddress.StateProvince.Name));
                script = script.Replace("{COUNTRY}", order.BillingAddress == null || order.BillingAddress.Country == null ? "" : FixIllegalJavaScriptChars(order.BillingAddress.Country.Name));
                script = script.Replace("{PAYMENTMETHOD}", GetLabelForValue(order.PaymentMethodSystemName, paymentMethodsLabels));
                script = script.Replace("{SHIPPINGMETHOD}", GetLabelForValue(order.ShippingMethod, shippingMethodsLabels));

                string detailScriptOriginal = detailScript;//Keeps the model clean for each loop
                var detailsScriptBuilder = new StringBuilder();
                foreach (var item in order.OrderItems)
                {
                    detailScript = detailScriptOriginal;
                    //get category
                    string categ = "";
                    var defaultProductCategory = _categoryService.GetProductCategoriesByProductId(item.Product.Id).FirstOrDefault();
                    if (defaultProductCategory != null)
                        categ = defaultProductCategory.Category.Name;
                    detailScript = detailScript.Replace("{ORDERID}", item.OrderId.ToString());
                    detailScript = detailScript.Replace("{PRODUCTSKU}", FixIllegalJavaScriptChars(item.Product.Sku));
                    detailScript = detailScript.Replace("{PRODUCTNAME}", FixIllegalJavaScriptChars(item.Product.Name));
                    detailScript = detailScript.Replace("{CATEGORYNAME}", FixIllegalJavaScriptChars(categ));
                    detailScript = detailScript.Replace("{UNITPRICEEXCLTAX}", item.UnitPriceExclTax.ToString("0.00", usCulture));
                    detailScript = detailScript.Replace("{UNITPRICEINCLTAX}", item.UnitPriceInclTax.ToString("0.00", usCulture));
                    detailScript = detailScript.Replace("{QUANTITY}", item.Quantity.ToString());
                    detailsScriptBuilder.AppendLine(detailScript);
                }
                script = script.Replace("{ORDER_ITEMS_SCRIPT}", detailsScriptBuilder.ToString());
            }

            script = script.Replace("{FREETOKEN}", freeToken);
            script = script.Replace("{SITE}", _webHelper.GetStoreLocation().Replace("http://", "").Replace("/", ""));
           
            
            if (this.HttpContext.Request.Url.Scheme == "https")
                script = script.Replace("http://", "https://");

            return script;
        }

        private string FixIllegalJavaScriptChars(string text)
        {
            if (String.IsNullOrEmpty(text))
                return text;

            //replace ' with \' (http://stackoverflow.com/questions/4292761/need-to-url-encode-labels-when-tracking-events-with-google-analytics)
            text = text.Replace("'", "\\'");
            return text;
        }




    }
}